/*
 * jsocket.cc
 *
 *  Created on: Dec 2, 2010
 *      Author: fify
 */

#include "jsocket.h"
#include "utils.h"
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <iostream>
#include <sstream>

using std::cout;
using std::endl;
using std::string;
using std::istringstream;

namespace CncrJConfig
{

int JSocket::conn_jos(string host, int port)
{
	struct sockaddr_in serv_addr;

	if(m_sock >= 0)
	{
		close(m_sock);
		m_sock = -1;
	}

	if((m_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("Socket init error");
		return -1;
	}

	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	serv_addr.sin_addr.s_addr = inet_addr(host.c_str());
	bzero(&(serv_addr.sin_zero), 8);

	if(connect(m_sock, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr)) < 0)
	{
		perror("Connecting to server error");
		return -1;
	}

	return m_sock;
}

int JSocket::send_msg(string msg)
{
#ifdef DEBUG
	std::cout << "Sending: " << msg << std::endl;
	std::cout << "Socket number: " << (int) m_sock << endl;
#endif
	consume(m_avail);

	int nwrote = 0;

	msg += "\r";

	while((nwrote = send(m_sock, msg.c_str(), msg.length(), 0)) < 0)
	{
		perror("Send message error");
		if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
			continue;
		else
			return -1;
	}

	return nwrote;
}
/*
int JSocket::recv_msg(string &msg)
{
	return read_line(msg);
}*/

//int JSocket::read_line(string &result)
//{
//	result.clear();
//
//	int navail, nread = 0;
//	char *line_end;
//
//	line_end = find_end_line(m_head);
//
//	if(line_end != NULL)
//	{
//		nread = line_end - m_head;
//		result.append(m_head, nread);
//		consume(nread);
//
//		return nread;
//	}
//
//	/*
//	 * Retry at most five times, then exits.
//	 * Wait at most 2 second for the next receive.
//	 */
//
//	navail = buf_recv();
//
//	if(navail == 0)
//	{
//		//cout << "Returning may be unreliable." << endl;
//		return nread;
//	}
//
//	line_end = find_end_line(m_head);
//
//	if(line_end == NULL)
//	{
//		//result.append(m_buf, navail);
//		//nread += navail;
//		//consume(navail);
//		return 0;
//	}
//
//	navail = line_end - m_head;
//	nread += navail;
//	result.append(m_head, navail);
//	consume(navail);
//
//	return nread;
//}

/*
int JSocket::recv_all(string &result)
{
	result.clear();

	buf_recv();

	int navail = m_avail;

	result.append(m_head, m_avail);
	consume(m_avail);

	return navail;
}*/
/*
int JSocket::recv_all(vector<string> &result)
{
	//printf("Recv All\n");
	string buf;
	int nread;

	if((nread = recv_all(buf)) <= 0)
		return 0;

	istringstream iss(buf);
	//iss.init();

	char line[1024];

	while(iss.getline(line, 1024))
	{
		//cout << line << endl;
		string temp(line);

		for(int i = 0;i<(int)temp.length();i++)
		{
			if(temp[i] == '\n' || temp[i] == '\r')
				temp[i] = ' ';
		}

		string::size_type start, end;
		start = temp.find_first_not_of(' ', 0);
		end = temp.find_last_not_of(' ', string::npos);

		if(start == string::npos)
			continue;

		result.push_back(temp.substr(start, end - start + 1));
	}

#ifdef DEBUG
	printf("\033[32m" "Result size: %d\n" "\033[0m", (int)result.size());
	for(int i = 0;i<(int)result.size();i++)
		cout << result[i] << endl;
#endif

	return nread;
}*/

/*
int JSocket::recv_and_clear()
{
	buf_recv();

	int ret = m_avail;

	consume(m_avail);

	return ret;
}*/

#ifdef DEBUG
int JSocket::print_and_clear()
{
	vector<string> result;
	int ret = this->recv_all(result);
	for(int i = 0;i<(int)result.size();i++)
	{
		cout << result[i] << endl;
	}

	return ret;
}
#endif

//int JSocket::read_line_pure(string &result)
//{
//	int ret = read_line(result);
//
//	int len = result.length();
//
//	if(len >= 2 && result[len - 1] == '\n' && result[len - 2] == '\r')
//	{
//		result.erase(len - 2, 2);
//	}
//
//	return ret;
//}

// Remove all the playback characters.
//int JSocket::truncate_playback()
//{
//	string buf;
//
//	read_line(buf);
//
//	printf("Truncating: %s", buf.c_str());
//
//	return 0;
//}

void JSocket::consume(int nbytes)
{
	m_head += nbytes;
	m_avail -= nbytes;

	if(m_avail == 0)
	{
		reset();
	}
}

void JSocket::reset()
{
	m_head = m_buf;
	m_avail = 0;
	memset(m_buf, 0, sizeof(m_buf));
}

// Receive everything from the socket to the socket buffer.
//int JSocket::buf_recv()
//{
//	int nread = 0, res;
//
//	struct timeval tv;
//
//	tv.tv_sec = 0;
//	tv.tv_usec = 10000;
//
//	fd_set fds;
//
//	for(int i = 0;i<10;i++)
//	{
//		FD_SET(m_sock, &fds);
//
//		// Wait for at most 0.1 seconds.
//		select(m_sock + 1, &fds, NULL, NULL, &tv);
//
//		if(FD_ISSET(m_sock, &fds))
//		{
//			res = ::recv(m_sock, m_head + m_avail, BUFSIZE - (m_head + m_avail - m_buf) - 1, 0);
//			i = 0;
//			//break;
//		}
//		else
//			continue;
//
//		if(res < 0 && errno == EAGAIN)
//			continue;
//
//		if(res < 0)
//		{
//			break;
//		}
//
//		if(res > 0)
//		{
//			nread += res;
//			m_avail += res;
//			*(m_head + m_avail) = 0;
//#ifdef DEBUG
//			printf("Buf:\n");
//			printf("%s", m_buf);
//#endif
//			if(m_head + m_avail - m_buf >= BUFSIZE - 3)
//				consume(m_avail);
//		}
//	}
//
//	return nread;
//}

int JSocket::expect(string str)
{
#ifdef DEBUG
	cout << "Expecting: " << str << endl;
#endif
	vector<string> vec;
	vec.push_back(str);

	return expect(vec);
}

void JSocket::adjust()
{
	consume(m_avail / 2);

	for(int i = 0;i<m_avail;i++)
	{
		m_buf[i] = *(m_head + i);
	}
	m_head = m_buf;
}
// Timeout in msec.
int JSocket::expect(vector<string> &strs)
{
#ifdef DEBUG
	cout << "Expecting multiple strings:" << endl;
	for(unsigned i = 0;i<strs.size();++i)
		cout << "\t" << strs[i] << endl;
#endif
	int nread = 0, res;
	//struct timeval tv;

	//tv.tv_sec = timeout/1000;
	//tv.tv_usec = timeout % 1000 * 1000;

	//fd_set fds;

	while(1)
	{
#ifdef DEBUG
		printf("Doing wait in expect().\n");
		printf("Available buffer size: %d\n", BUFSIZE - (m_head + m_avail - m_buf) - 1);
#endif
		//FD_SET(m_sock, &fds);

		//int select_ret = select(m_sock + 1, &fds, NULL, NULL, &tv);
		//if(FD_ISSET(m_sock, &fds))
		//{
			res = ::recv(m_sock, m_head + m_avail, BUFSIZE - (m_head + m_avail - m_buf) - 1, 0);
			//break;
		//}
		//else if(select_ret <= 0)
		//{
			// The timeout event occurs.
			//printf("Timeout!!\n");
			//return -1;
		//}

		if(res < 0 && errno == EAGAIN)
			continue;

		if(res < 0)
		{
			break;
		}

		if(res > 0)
		{
			nread += res;
			m_avail += res;
			if(m_avail > BUFSIZE / 2)
				adjust();
			*(m_head + m_avail) = 0;
		}

		string buf(m_head, m_avail);
#ifdef DEBUG
		std::cout << "----------- buf ----------" << std::endl;
		std::cout << buf << std::endl;
		std::cout << "--------- buf end --------" << std::endl;
#endif

		for(int i = 0;i<(int)strs.size();i++)
		{
			if(buf.find(strs[i]) != string::npos)
			{
				consume(m_avail);
				return i;
			}
		}

		//printf("data available: %d\r", m_avail);
	//	if(m_head + m_avail - m_buf >= BUFSIZE - 5)
	//		consume(m_avail);
	}
	//printf("\n");

	consume(m_avail);

	return -1;
}

} // End of namespace;
